// Copyright 2014 Timothy Edward Baldwin

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


#ifndef NAVALIS_H
#define NAVALIS_H

#define PORT_6a44 htons(1027)
#define ECHO_MAGIC htonl(0x36613434)

#include <asm/byteorder.h>
#include <arpa/inet.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <netinet/ip.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <unistd.h>

#include <cstring>
#include <memory>

template <typename T>
inline void set_zero(T& x) {
  std::memset(&x, 0, sizeof(x));
}

inline struct sockaddr *sa_cast(struct sockaddr_in *in) {
  return reinterpret_cast<struct sockaddr *>(in);
}

inline struct sockaddr_in *sin_cast(struct sockaddr *in) {
  return reinterpret_cast<struct sockaddr_in *>(in);
}

template <typename T>
inline uint8_t *u8_cast(T *v) {
  return reinterpret_cast<uint8_t *>(v);
}

template <typename T>
inline const uint8_t *u8_cast(const T *v) {
  return reinterpret_cast<const uint8_t *>(v);
}

struct prefix {
  uint32_t x;
  uint16_t y;
  operator bool () {
    return x || y;
  }
} __attribute__((packed));

bool operator == (const prefix &a, const prefix &b) {
  return a.x == b.x && a.y == b.y;
}

struct ipv6_addr {
  struct prefix prefix;
  uint32_t public_ipv4;
  uint16_t mapped_port;
  uint32_t private_ipv4;
  operator bool () {
    return prefix.x || prefix.y || public_ipv4 || mapped_port || private_ipv4;
  }
} __attribute__((packed));

bool operator == (const ipv6_addr &a, const ipv6_addr &b) {
  return std::memcmp(&a, &b, sizeof(a)) == 0;
}

bool operator != (const ipv6_addr &a, const ipv6_addr &b) {
  return std::memcmp(&a, &b, sizeof(a)) != 0;
}

bool operator < (const ipv6_addr &a, const ipv6_addr &b) {
  return std::memcmp(&a, &b, sizeof(a)) < 0;
}

bool same_client(const ipv6_addr &a, const ipv6_addr &b) {
  return std::memcmp(&a, &b, 12) == 0;
}

struct ipv6_packet {
#ifdef __LITTLE_ENDIAN_BITFIELD
  uint8_t misc1:4, version:4;
#else
  uint8_t version:4, misc1:4;
#endif
  uint8_t misc2[3];
  uint16_t length;
  uint8_t next_header;
  uint8_t hoplimit;
  ipv6_addr source;
  ipv6_addr destination;
  uint8_t icmp_type;
  uint8_t icmp_code;
  uint16_t icmp_checksum;
  uint32_t pad;
  uint32_t echo_magic;
  uint64_t nounce;
  int size() { return 40 + ntohs(length); }
} __attribute__((packed, aligned(4)));

union bubble_t {
  struct {
    uint8_t prefix[12];
    uint64_t nounce;
  } __attribute__((packed, aligned(4)));
  ipv6_addr address;
};

union udp_packet {
  ipv6_packet v6;
  bubble_t bubble;
  uint8_t pad[1500];
};

extern int rand_fd;

template <typename T>
inline void set_random(T& x) {
  read(rand_fd, &x, sizeof(x));
}

#endif // IPV6_ANYWHERE_H
